package main.database;

import com.mongodb.client.AggregateIterable;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.model.Aggregates;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Projections;
import main.GlobalConfig;
import main.StringHelper;
import main.TimeHelper;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
import static com.mongodb.client.model.Filters.eq;
import static com.mongodb.client.model.Filters.and;
import static com.mongodb.client.model.Filters.gte;
import static com.mongodb.client.model.Filters.ne;
import static com.mongodb.client.model.Updates.combine;
import static com.mongodb.client.model.Updates.set;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;
import java.util.regex.Pattern;

public class ArticleHelper {
    /**
     * 随机得到一定数量的小说信息
     * @param count，指定的数量
     * @return 信息列表（List），列表中每个元素都包含（依次）：id, title, score
     */
    public static List<String[]> getRandomCountArticles(int count) {
        MongoCollection<Document> collection = DatabaseMan.Instance().GetCollection(GlobalConfig.collectionNameOfArticle);
        AggregateIterable<Document> documents = collection.aggregate(Arrays.asList(Aggregates.sample(count)));
        MongoCursor<Document> cursor = documents.iterator();

        List<String[]> resultList = new ArrayList<>();
        while (cursor.hasNext()) {
            Document doc = cursor.next();

            String[] strs = new String[3]; // id, title, score
            strs[0] = doc.get("_id").toString();
            strs[1] = doc.get("title").toString();
            double dScore = (double) doc.get("score");
            strs[2] = ScoreHelper.getFormattedSecretScore(dScore);

            resultList.add(strs);
        }
        cursor.close();

        return resultList;
    }

    /**
     * 根据指定的catlog和score，得到小说列表。必须是同样的catlog，score必须比指定的高
     * @param catlog
     * @param score
     * @return 小说信息列表，每个元素都包含（依次）：id, title, score
     */
    public static List<String[]> getSameCatlogAndScoreAbove(String catlog, Double score, String excludeFictionId) {
        Bson filter = and(eq("catlog", catlog), gte("score", score), ne("_id", new ObjectId(excludeFictionId)));

        MongoCollection<Document> collection = DatabaseMan.Instance().GetCollection(GlobalConfig.collectionNameOfArticle);
        FindIterable<Document> documents = collection.find(filter).limit(30);
        MongoCursor<Document> cursor = documents.iterator();

        List<String[]> resultList = new ArrayList<>();
        while(cursor.hasNext()) {
            Document doc = cursor.next();

            String[] strs = new String[3];
            strs[0] = doc.get("_id").toString();
            strs[1] = doc.get("title").toString();
            double dScore = (double) doc.get("score");
            strs[2] = ScoreHelper.getFormattedSecretScore(dScore);

            resultList.add(strs);
        }
        cursor.close();

        return resultList;
    }

    /**
     * 指定一个小说id，得到该小说的部分特定信息
     * @param id
     * @return 字符串数组，元素依次为：title, description, rootcatlog, catlog, finishyear, author
     */
    public static String[] getFieldsFromId(String id) {
        Bson filter = Filters.eq("_id", new ObjectId(id));
        MongoCollection<Document> collection = DatabaseMan.Instance().GetCollection(GlobalConfig.collectionNameOfArticle);
        FindIterable<Document> documents = collection.find(filter).projection(Projections.include("title",
                "description", "rootcatlog", "catlog", "finishyear", "author", "finished"));
        MongoCursor<Document> cursor = documents.iterator();

        String[] strs = null;
        if (cursor.hasNext()) {
            Document doc = cursor.next();

            strs = new String[7];
            strs[0] = doc.get("title").toString();
            strs[1] = doc.get("description").toString();
            strs[2] = doc.get("rootcatlog").toString();
            strs[3] = doc.get("catlog").toString();
            strs[4] = doc.get("finishyear").toString();
            strs[5] = doc.get("author").toString();
            strs[6] = doc.get("finished").toString();
        }
        cursor.close();

        return strs;
    }

    /**
     * 指定一个小说id，得到该小说的部分特定信息
     * @param id
     * @return 字符串数组，元素依次为：title, author, score
     */
    public static String[] getFieldsFromId2(String id, boolean encryptScore) {
        Bson filter = Filters.eq("_id", new ObjectId(id));
        MongoCollection<Document> collection = DatabaseMan.Instance().GetCollection(GlobalConfig.collectionNameOfArticle);
        FindIterable<Document> documents = collection.find(filter).projection(Projections.include("title", "author", "score"));
        MongoCursor<Document> cursor = documents.iterator();

        String[] strs = null;
        if (cursor.hasNext()) {
            Document doc = cursor.next();

            strs = new String[3];
            strs[0] = doc.get("title").toString();
            strs[1] = doc.get("author").toString();
            double dScore = (double) doc.get("score");
            if (encryptScore) {
                strs[2] = ScoreHelper.getFormattedSecretScore(dScore);
            }
            else {
                strs[2] = ScoreHelper.getFormattedScore(dScore);
            }
        }
        cursor.close();

        return strs;
    }

    /**
     * 查找title中含有指定关键字的特定小说
     * @param key
     * @return，列表，每个元素包含（依次）：title, description, catlog, score, id
     */
    public static List<String[]> findArticles(String key) {
        MongoCollection<Document> collection = DatabaseMan.Instance().GetCollection(GlobalConfig.collectionNameOfArticle);
        String regex = "\\B" + key + "|" + key + "\\B" + "|" + key;
        Pattern pattern = Pattern.compile(regex);
        FindIterable<Document> findIterable = collection.find(Filters.regex("title", pattern)).
                projection(Projections.include("title", "description", "catlog", "score"));
        MongoCursor<Document> cursor = findIterable.iterator();

        List<String[]> resultList = new ArrayList<>();
        while(cursor.hasNext()) {
            Document doc = cursor.next();

            String title = doc.get("title").toString();
            String description = doc.get("description").toString();
            String catlog = doc.get("catlog").toString();
            double dScore = (double) doc.get("score");
            String score = ScoreHelper.getFormattedSecretScore(dScore);
            String id = doc.get("_id").toString();

            String[] infos = new String[5];
            infos[0] = title;
            infos[1] = description;
            infos[2] = catlog;
            infos[3] = score;
            infos[4] = id;

            resultList.add(infos);
        }
        cursor.close();

        return resultList;
    }

    /**
     * 从小说的pinyin字段搜索符合条件（首字母符合参数）的记录
     * @param startAlphabet
     * @return
     */
    public static List<String[]> findArticleFromPinYin(String startAlphabet) {
        List<String[]> resultList = new ArrayList<>();
        MongoCollection<Document> collection = DatabaseMan.Instance().GetCollection(GlobalConfig.collectionNameOfArticle);
        if (startAlphabet.length() != 0) {
            String regex = "^" + startAlphabet;
            Pattern pattern = Pattern.compile(regex);
            Bson sortMethod = new Document("pinyin", 1);
            FindIterable<Document> findIterable = collection.find(Filters.regex("pinyin", pattern))
                    .projection(Projections.include("title", "_id", "finishyear", "pinyin"))
                    .sort(sortMethod);
            MongoCursor<Document> cursor = findIterable.iterator();

            while(cursor.hasNext()) {
                Document doc = cursor.next();

                String[] result = new String[4];
                result[0] = doc.get("title").toString();
                result[1] = doc.get("_id").toString();
                result[2] = doc.get("finishyear").toString();
                result[3] = doc.get("pinyin").toString();

                resultList.add(result);
            }
            cursor.close();
        }
        return resultList;
    }

    /**
     * 更新指定文章的内容
     * @param id，文章的ID（字符串），需要通过ObjectId转化为mongodb的
     * @param title
     * @param author
     * @param description
     * @param finishYear
     */
    public static void updateArticle(String id, String title, String author, String description, String finishYear, boolean finished) {
        MongoCollection<Document> collection = DatabaseMan.Instance().GetCollection(GlobalConfig.collectionNameOfArticle);
        FindIterable<Document> findIterable = collection.find(Filters.eq("_id", new ObjectId(id)));
        MongoCursor<Document> cursor = findIterable.iterator();

        if (cursor.hasNext()) {
            description = StringHelper.replaceReturnWithBR(description);
            Bson filter = eq("_id", new ObjectId(id));
            Bson updates = combine(set("title", title), set("author", author), set("description", description), set("finishyear", finishYear), set("finished", finished));
            collection.updateOne(filter, updates);
        }

        cursor.close();
    }

    /**
     * 按时间新旧取记录，比如取当月或当周、当天的记录
     * @param limit 需要取多少个
     * @return
     */
    public static List<String[]> findArtileListFromTime(int limit) {
        List<String[]> result = new ArrayList<>();

        MongoCollection<Document> collection = DatabaseMan.Instance().GetCollection(GlobalConfig.collectionNameOfArticle);
        Bson sortMethod = new Document("createtime", -1);
        FindIterable<Document> findIterable = collection.find().projection(Projections.include("title", "_id", "rootcatlog", "createtime")).sort(sortMethod);
        if (limit != 0) {
            findIterable = findIterable.limit(limit);
        }
        MongoCursor<Document> cursor = findIterable.iterator();

        if (limit == 0) limit = Integer.MAX_VALUE;

        int count = 0;
        while (count < limit && cursor.hasNext()) {
            Document doc = cursor.next();
            String createTimeStr = doc.get("createtime").toString();
            Timestamp createTime = Timestamp.valueOf(createTimeStr);
            boolean within = TimeHelper.isTimeWithinWeeks(createTime, GlobalConfig.getInformationTimeRange());
            if (!within) break;

            String[] value = new String[4];
            value[0] = doc.get("_id").toString();
            value[1] = doc.get("title").toString();
            value[2] = doc.get("rootcatlog").toString();
            value[3] = createTimeStr.split(" ")[0];

            result.add(value);

            count++;
        }
        cursor.close();

        return result;
    }
}
